/**
 * jquery.Jcrop.js v0.9.9
 * jQuery Image Cropping Plugin
 * @author Kelly Hallman <khallman@gmail.com>
 * Copyright (c) 2008-2011 Kelly Hallman - released under MIT License {{{
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * }}}
 */

(function($) {

    $.Jcrop = function(obj, opt) {
        var options = $.extend({}, $.Jcrop.defaults),
                docOffset, lastcurs, ie6mode = false;

        // Internal Methods {{{
        function px(n) {
            return parseInt(n, 10) + 'px';
        }
        function pct(n) {
            return parseInt(n, 10) + '%';
        }
        function cssClass(cl) {
            return options.baseClass + '-' + cl;
        }
        function supportsColorFade() {
            return $.fx.step.hasOwnProperty('backgroundColor');
        }
        function getPos(obj) //{{{
        {
            // Updated in v0.9.4 to use built-in dimensions plugin
            var pos = $(obj).offset();
            return [pos.left, pos.top];
        }
        //}}}
        function mouseAbs(e) //{{{
        {
            return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
        }
        //}}}
        function setOptions(opt) //{{{
        {
            if (typeof (opt) !== 'object') {
                opt = {};
            }
            options = $.extend(options, opt);

            if (typeof (options.onChange) !== 'function') {
                options.onChange = function() {
                };
            }
            if (typeof (options.onSelect) !== 'function') {
                options.onSelect = function() {
                };
            }
            if (typeof (options.onRelease) !== 'function') {
                options.onRelease = function() {
                };
            }
        }
        //}}}
        function myCursor(type) //{{{
        {
            if (type !== lastcurs) {
                Tracker.setCursor(type);
                lastcurs = type;
            }
        }
        //}}}
        function startDragMode(mode, pos) //{{{
        {
            docOffset = getPos($img);
            Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');

            if (mode === 'move') {
                return Tracker.activateHandlers(createMover(pos), doneSelect);
            }

            var fc = Coords.getFixed();
            var opp = oppLockCorner(mode);
            var opc = Coords.getCorner(oppLockCorner(opp));

            Coords.setPressed(Coords.getCorner(opp));
            Coords.setCurrent(opc);

            Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect);
        }
        //}}}
        function dragmodeHandler(mode, f) //{{{
        {
            return function(pos) {
                if (!options.aspectRatio) {
                    switch (mode) {
                        case 'e':
                            pos[1] = f.y2;
                            break;
                        case 'w':
                            pos[1] = f.y2;
                            break;
                        case 'n':
                            pos[0] = f.x2;
                            break;
                        case 's':
                            pos[0] = f.x2;
                            break;
                    }
                } else {
                    switch (mode) {
                        case 'e':
                            pos[1] = f.y + 1;
                            break;
                        case 'w':
                            pos[1] = f.y + 1;
                            break;
                        case 'n':
                            pos[0] = f.x + 1;
                            break;
                        case 's':
                            pos[0] = f.x + 1;
                            break;
                    }
                }
                Coords.setCurrent(pos);
                Selection.update();
            };
        }
        //}}}
        function createMover(pos) //{{{
        {
            var lloc = pos;
            KeyManager.watchKeys();

            return function(pos) {
                Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
                lloc = pos;

                Selection.update();
            };
        }
        //}}}
        function oppLockCorner(ord) //{{{
        {
            switch (ord) {
                case 'n':
                    return 'sw';
                case 's':
                    return 'nw';
                case 'e':
                    return 'nw';
                case 'w':
                    return 'ne';
                case 'ne':
                    return 'sw';
                case 'nw':
                    return 'se';
                case 'se':
                    return 'nw';
                case 'sw':
                    return 'ne';
            }
        }
        //}}}
        function createDragger(ord) //{{{
        {
            return function(e) {
                if (options.disabled) {
                    return false;
                }
                if ((ord === 'move') && !options.allowMove) {
                    return false;
                }
                btndown = true;
                startDragMode(ord, mouseAbs(e));
                e.stopPropagation();
                e.preventDefault();
                return false;
            };
        }
        //}}}
        function presize($obj, w, h) //{{{
        {
            var nw = $obj.width(),
                    nh = $obj.height();
            if ((nw > w) && w > 0) {
                nw = w;
                nh = (w / $obj.width()) * $obj.height();
            }
            if ((nh > h) && h > 0) {
                nh = h;
                nw = (h / $obj.height()) * $obj.width();
            }
            xscale = $obj.width() / nw;
            yscale = $obj.height() / nh;
            $obj.width(nw).height(nh);
        }
        //}}}
        function unscale(c) //{{{
        {
            return {
                x: parseInt(c.x * xscale, 10),
                y: parseInt(c.y * yscale, 10),
                x2: parseInt(c.x2 * xscale, 10),
                y2: parseInt(c.y2 * yscale, 10),
                w: parseInt(c.w * xscale, 10),
                h: parseInt(c.h * yscale, 10)
            };
        }
        //}}}
        function doneSelect(pos) //{{{
        {
            var c = Coords.getFixed();
            if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
                Selection.enableHandles();
                Selection.done();
            } else {
                Selection.release();
            }
            Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
        }
        //}}}
        function newSelection(e) //{{{
        {
            if (options.disabled) {
                return false;
            }
            if (!options.allowSelect) {
                return false;
            }
            btndown = true;
            docOffset = getPos($img);
            Selection.disableHandles();
            myCursor('crosshair');
            var pos = mouseAbs(e);
            Coords.setPressed(pos);
            Selection.update();
            Tracker.activateHandlers(selectDrag, doneSelect);
            KeyManager.watchKeys();

            e.stopPropagation();
            e.preventDefault();
            return false;
        }
        //}}}
        function selectDrag(pos) //{{{
        {
            Coords.setCurrent(pos);
            Selection.update();
        }
        //}}}
        function newTracker() //{{{
        {
            var trk = $('<div></div>').addClass(cssClass('tracker'));
            if ($.browser.msie) {
                trk.css({
                    opacity: 0,
                    backgroundColor: 'white'
                });
            }
            return trk;
        }
        //}}}

        // }}}
        // Initialization {{{
        // Sanitize some options {{{
        if ($.browser.msie && ($.browser.version.split('.')[0] === '6')) {
            ie6mode = true;
        }
        if (typeof (obj) !== 'object') {
            obj = $(obj)[0];
        }
        if (typeof (opt) !== 'object') {
            opt = {};
        }
        // }}}
        setOptions(opt);
        // Initialize some jQuery objects {{{
        // The values are SET on the image(s) for the interface
        // If the original image has any of these set, they will be reset
        // However, if you destroy() the Jcrop instance the original image's
        // character in the DOM will be as you left it.
        var img_css = {
            border: 'none',
            margin: 0,
            padding: 0,
            position: 'absolute'
        };

        var $origimg = $(obj);
        var $img = $origimg.clone().removeAttr('id').css(img_css);

        $img.width($origimg.width());
        $img.height($origimg.height());
        $origimg.after($img).hide();

        presize($img, options.boxWidth, options.boxHeight);

        var boundx = $img.width(),
                boundy = $img.height(),
                $div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
            position: 'relative',
            backgroundColor: options.bgColor
        }).insertAfter($origimg).append($img);

        delete(options.bgColor);
        if (options.addClass) {
            $div.addClass(options.addClass);
        }

        var $img2 = $('<img />')
                .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),
                $img_holder = $('<div />')
                .width(pct(100)).height(pct(100)).css({
            zIndex: 310,
            position: 'absolute',
            overflow: 'hidden'
        }).append($img2),
                $hdl_holder = $('<div />')
                .width(pct(100)).height(pct(100)).css('zIndex', 320),
                $sel = $('<div />')
                .css({
                    position: 'absolute',
                    zIndex: 300
                }).insertBefore($img).append($img_holder, $hdl_holder);

        if (ie6mode) {
            $sel.css({
                overflowY: 'hidden'
            });
        }

        var bound = options.boundary;
        var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
            position: 'absolute',
            top: px(-bound),
            left: px(-bound),
            zIndex: 290
        }).mousedown(newSelection);

        /* }}} */
        // Set more variables {{{
        var bgopacity = options.bgOpacity,
                xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true,
                btndown, animating, shift_down;

        docOffset = getPos($img);
        // }}}
        // }}}
        // Internal Modules {{{
        // Touch Module {{{ 
        var Touch = (function() {
            // Touch support detection function adapted (under MIT License)
            // from code by Jeffrey Sambells - http://github.com/iamamused/
            function hasTouchSupport() {
                var support = {},
                        events = ['touchstart', 'touchmove', 'touchend'],
                        el = document.createElement('div'), i;

                try {
                    for (i = 0; i < events.length; i++) {
                        var eventName = events[i];
                        eventName = 'on' + eventName;
                        var isSupported = (eventName in el);
                        if (!isSupported) {
                            el.setAttribute(eventName, 'return;');
                            isSupported = typeof el[eventName] == 'function';
                        }
                        support[events[i]] = isSupported;
                    }
                    return support.touchstart && support.touchend && support.touchmove;
                }
                catch (err) {
                    return false;
                }
            }

            function detectSupport() {
                if ((options.touchSupport === true) || (options.touchSupport === false))
                    return options.touchSupport;
                else
                    return hasTouchSupport();
            }
            return {
                createDragger: function(ord) {
                    return function(e) {
                        e.pageX = e.originalEvent.changedTouches[0].pageX;
                        e.pageY = e.originalEvent.changedTouches[0].pageY;
                        if (options.disabled) {
                            return false;
                        }
                        if ((ord === 'move') && !options.allowMove) {
                            return false;
                        }
                        btndown = true;
                        startDragMode(ord, mouseAbs(e));
                        e.stopPropagation();
                        e.preventDefault();
                        return false;
                    };
                },
                newSelection: function(e) {
                    e.pageX = e.originalEvent.changedTouches[0].pageX;
                    e.pageY = e.originalEvent.changedTouches[0].pageY;
                    return newSelection(e);
                },
                isSupported: hasTouchSupport,
                support: detectSupport()
            };
        }());
        // }}}
        // Coords Module {{{
        var Coords = (function() {
            var x1 = 0,
                    y1 = 0,
                    x2 = 0,
                    y2 = 0,
                    ox, oy;

            function setPressed(pos) //{{{
            {
                pos = rebound(pos);
                x2 = x1 = pos[0];
                y2 = y1 = pos[1];
            }
            //}}}
            function setCurrent(pos) //{{{
            {
                pos = rebound(pos);
                ox = pos[0] - x2;
                oy = pos[1] - y2;
                x2 = pos[0];
                y2 = pos[1];
            }
            //}}}
            function getOffset() //{{{
            {
                return [ox, oy];
            }
            //}}}
            function moveOffset(offset) //{{{
            {
                var ox = offset[0],
                        oy = offset[1];

                if (0 > x1 + ox) {
                    ox -= ox + x1;
                }
                if (0 > y1 + oy) {
                    oy -= oy + y1;
                }

                if (boundy < y2 + oy) {
                    oy += boundy - (y2 + oy);
                }
                if (boundx < x2 + ox) {
                    ox += boundx - (x2 + ox);
                }

                x1 += ox;
                x2 += ox;
                y1 += oy;
                y2 += oy;
            }
            //}}}
            function getCorner(ord) //{{{
            {
                var c = getFixed();
                switch (ord) {
                    case 'ne':
                        return [c.x2, c.y];
                    case 'nw':
                        return [c.x, c.y];
                    case 'se':
                        return [c.x2, c.y2];
                    case 'sw':
                        return [c.x, c.y2];
                }
            }
            //}}}
            function getFixed() //{{{
            {
                if (!options.aspectRatio) {
                    return getRect();
                }
                // This function could use some optimization I think...
                var aspect = options.aspectRatio,
                        min_x = options.minSize[0] / xscale,
                        //min_y = options.minSize[1]/yscale,
                        max_x = options.maxSize[0] / xscale,
                        max_y = options.maxSize[1] / yscale,
                        rw = x2 - x1,
                        rh = y2 - y1,
                        rwa = Math.abs(rw),
                        rha = Math.abs(rh),
                        real_ratio = rwa / rha,
                        xx, yy;

                if (max_x === 0) {
                    max_x = boundx * 10;
                }
                if (max_y === 0) {
                    max_y = boundy * 10;
                }
                if (real_ratio < aspect) {
                    yy = y2;
                    w = rha * aspect;
                    xx = rw < 0 ? x1 - w : w + x1;

                    if (xx < 0) {
                        xx = 0;
                        h = Math.abs((xx - x1) / aspect);
                        yy = rh < 0 ? y1 - h : h + y1;
                    } else if (xx > boundx) {
                        xx = boundx;
                        h = Math.abs((xx - x1) / aspect);
                        yy = rh < 0 ? y1 - h : h + y1;
                    }
                } else {
                    xx = x2;
                    h = rwa / aspect;
                    yy = rh < 0 ? y1 - h : y1 + h;
                    if (yy < 0) {
                        yy = 0;
                        w = Math.abs((yy - y1) * aspect);
                        xx = rw < 0 ? x1 - w : w + x1;
                    } else if (yy > boundy) {
                        yy = boundy;
                        w = Math.abs(yy - y1) * aspect;
                        xx = rw < 0 ? x1 - w : w + x1;
                    }
                }

                // Magic %-)
                if (xx > x1) { // right side
                    if (xx - x1 < min_x) {
                        xx = x1 + min_x;
                    } else if (xx - x1 > max_x) {
                        xx = x1 + max_x;
                    }
                    if (yy > y1) {
                        yy = y1 + (xx - x1) / aspect;
                    } else {
                        yy = y1 - (xx - x1) / aspect;
                    }
                } else if (xx < x1) { // left side
                    if (x1 - xx < min_x) {
                        xx = x1 - min_x;
                    } else if (x1 - xx > max_x) {
                        xx = x1 - max_x;
                    }
                    if (yy > y1) {
                        yy = y1 + (x1 - xx) / aspect;
                    } else {
                        yy = y1 - (x1 - xx) / aspect;
                    }
                }

                if (xx < 0) {
                    x1 -= xx;
                    xx = 0;
                } else if (xx > boundx) {
                    x1 -= xx - boundx;
                    xx = boundx;
                }

                if (yy < 0) {
                    y1 -= yy;
                    yy = 0;
                } else if (yy > boundy) {
                    y1 -= yy - boundy;
                    yy = boundy;
                }

                return makeObj(flipCoords(x1, y1, xx, yy));
            }
            //}}}
            function rebound(p) //{{{
            {
                if (p[0] < 0) {
                    p[0] = 0;
                }
                if (p[1] < 0) {
                    p[1] = 0;
                }

                if (p[0] > boundx) {
                    p[0] = boundx;
                }
                if (p[1] > boundy) {
                    p[1] = boundy;
                }

                return [p[0], p[1]];
            }
            //}}}
            function flipCoords(x1, y1, x2, y2) //{{{
            {
                var xa = x1,
                        xb = x2,
                        ya = y1,
                        yb = y2;
                if (x2 < x1) {
                    xa = x2;
                    xb = x1;
                }
                if (y2 < y1) {
                    ya = y2;
                    yb = y1;
                }
                return [Math.round(xa), Math.round(ya), Math.round(xb), Math.round(yb)];
            }
            //}}}
            function getRect() //{{{
            {
                var xsize = x2 - x1,
                        ysize = y2 - y1,
                        delta;

                if (xlimit && (Math.abs(xsize) > xlimit)) {
                    x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
                }
                if (ylimit && (Math.abs(ysize) > ylimit)) {
                    y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
                }

                if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
                    y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
                }
                if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
                    x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
                }

                if (x1 < 0) {
                    x2 -= x1;
                    x1 -= x1;
                }
                if (y1 < 0) {
                    y2 -= y1;
                    y1 -= y1;
                }
                if (x2 < 0) {
                    x1 -= x2;
                    x2 -= x2;
                }
                if (y2 < 0) {
                    y1 -= y2;
                    y2 -= y2;
                }
                if (x2 > boundx) {
                    delta = x2 - boundx;
                    x1 -= delta;
                    x2 -= delta;
                }
                if (y2 > boundy) {
                    delta = y2 - boundy;
                    y1 -= delta;
                    y2 -= delta;
                }
                if (x1 > boundx) {
                    delta = x1 - boundy;
                    y2 -= delta;
                    y1 -= delta;
                }
                if (y1 > boundy) {
                    delta = y1 - boundy;
                    y2 -= delta;
                    y1 -= delta;
                }

                return makeObj(flipCoords(x1, y1, x2, y2));
            }
            //}}}
            function makeObj(a) //{{{
            {
                return {
                    x: a[0],
                    y: a[1],
                    x2: a[2],
                    y2: a[3],
                    w: a[2] - a[0],
                    h: a[3] - a[1]
                };
            }
            //}}}

            return {
                flipCoords: flipCoords,
                setPressed: setPressed,
                setCurrent: setCurrent,
                getOffset: getOffset,
                moveOffset: moveOffset,
                getCorner: getCorner,
                getFixed: getFixed
            };
        }());

        //}}}
        // Selection Module {{{
        var Selection = (function() {
            var awake, hdep = 370;
            var borders = {};
            var handle = {};
            var seehandles = false;
            var hhs = options.handleOffset;

            // Private Methods
            function insertBorder(type) //{{{
            {
                var jq = $('<div />').css({
                    position: 'absolute',
                    opacity: options.borderOpacity
                }).addClass(cssClass(type));
                $img_holder.append(jq);
                return jq;
            }
            //}}}
            function dragDiv(ord, zi) //{{{
            {
                var jq = $('<div />').mousedown(createDragger(ord)).css({
                    cursor: ord + '-resize',
                    position: 'absolute',
                    zIndex: zi
                });

                if (Touch.support) {
                    jq.bind('touchstart', Touch.createDragger(ord));
                }

                $hdl_holder.append(jq);
                return jq;
            }
            //}}}
            function insertHandle(ord) //{{{
            {
                return dragDiv(ord, hdep++).css({
                    top: px(-hhs + 1),
                    left: px(-hhs + 1),
                    opacity: options.handleOpacity
                }).addClass(cssClass('handle'));
            }
            //}}}
            function insertDragbar(ord) //{{{
            {
                var s = options.handleSize,
                        h = s,
                        w = s,
                        t = hhs,
                        l = hhs;

                switch (ord) {
                    case 'n':
                    case 's':
                        w = pct(100);
                        break;
                    case 'e':
                    case 'w':
                        h = pct(100);
                        break;
                }

                return dragDiv(ord, hdep++).width(w).height(h).css({
                    top: px(-t + 1),
                    left: px(-l + 1)
                });
            }
            //}}}
            function createHandles(li) //{{{
            {
                var i;
                for (i = 0; i < li.length; i++) {
                    handle[li[i]] = insertHandle(li[i]);
                }
            }
            //}}}
            function moveHandles(c) //{{{
            {
                var midvert = Math.round((c.h / 2) - hhs),
                        midhoriz = Math.round((c.w / 2) - hhs),
                        north = -hhs + 1,
                        west = -hhs + 1,
                        east = c.w - hhs,
                        south = c.h - hhs,
                        x, y;

                if (handle.e) {
                    handle.e.css({
                        top: px(midvert),
                        left: px(east)
                    });
                    handle.w.css({
                        top: px(midvert)
                    });
                    handle.s.css({
                        top: px(south),
                        left: px(midhoriz)
                    });
                    handle.n.css({
                        left: px(midhoriz)
                    });
                }
                if (handle.ne) {
                    handle.ne.css({
                        left: px(east)
                    });
                    handle.se.css({
                        top: px(south),
                        left: px(east)
                    });
                    handle.sw.css({
                        top: px(south)
                    });
                }
                if (handle.b) {
                    handle.b.css({
                        top: px(south)
                    });
                    handle.r.css({
                        left: px(east)
                    });
                }
            }
            //}}}
            function moveto(x, y) //{{{
            {
                $img2.css({
                    top: px(-y),
                    left: px(-x)
                });
                $sel.css({
                    top: px(y),
                    left: px(x)
                });
            }
            //}}}
            function resize(w, h) //{{{
            {
                $sel.width(w).height(h);
            }
            //}}}
            function refresh() //{{{
            {
                var c = Coords.getFixed();

                Coords.setPressed([c.x, c.y]);
                Coords.setCurrent([c.x2, c.y2]);

                updateVisible();
            }
            //}}}

            // Internal Methods
            function updateVisible() //{{{
            {
                if (awake) {
                    return update();
                }
            }
            //}}}
            function update() //{{{
            {
                var c = Coords.getFixed();

                resize(c.w, c.h);
                moveto(c.x, c.y);

                /*
                 options.drawBorders &&
                 borders.right.css({ left: px(c.w-1) }) &&
                 borders.bottom.css({ top: px(c.h-1) });
                 */

                if (seehandles) {
                    moveHandles(c);
                }
                if (!awake) {
                    show();
                }

                options.onChange.call(api, unscale(c));
            }
            //}}}
            function show() //{{{
            {
                $sel.show();

                if (options.bgFade) {
                    $img.fadeTo(options.fadeTime, bgopacity);
                } else {
                    $img.css('opacity', bgopacity);
                }

                awake = true;
            }
            //}}}
            function release() //{{{
            {
                disableHandles();
                $sel.hide();

                if (options.bgFade) {
                    $img.fadeTo(options.fadeTime, 1);
                } else {
                    $img.css('opacity', 1);
                }

                awake = false;
                options.onRelease.call(api);
            }
            //}}}
            function showHandles() //{{{
            {
                if (seehandles) {
                    moveHandles(Coords.getFixed());
                    $hdl_holder.show();
                }
            }
            //}}}
            function enableHandles() //{{{
            {
                seehandles = true;
                if (options.allowResize) {
                    moveHandles(Coords.getFixed());
                    $hdl_holder.show();
                    return true;
                }
            }
            //}}}
            function disableHandles() //{{{
            {
                seehandles = false;
                $hdl_holder.hide();
            }
            //}}}
            function animMode(v) //{{{
            {
                if (animating === v) {
                    disableHandles();
                } else {
                    enableHandles();
                }
            }
            //}}}
            function done() //{{{
            {
                animMode(false);
                refresh();
            }
            //}}}
            /* Insert draggable elements {{{*/

            // Insert border divs for outline
            if (options.drawBorders) {
                borders = {
                    top: insertBorder('hline'),
                    bottom: insertBorder('hline bottom'),
                    left: insertBorder('vline'),
                    right: insertBorder('vline right')
                };
            }

            // Insert handles on edges
            if (options.dragEdges) {
                handle.t = insertDragbar('n');
                handle.b = insertDragbar('s');
                handle.r = insertDragbar('e');
                handle.l = insertDragbar('w');
            }

            // Insert side and corner handles
            if (options.sideHandles) {
                createHandles(['n', 's', 'e', 'w']);
            }
            if (options.cornerHandles) {
                createHandles(['sw', 'nw', 'ne', 'se']);
            }


            //}}}

            var $track = newTracker().mousedown(createDragger('move')).css({
                cursor: 'move',
                position: 'absolute',
                zIndex: 360
            });

            if (Touch.support) {
                $track.bind('touchstart.jcrop', Touch.createDragger('move'));
            }

            $img_holder.append($track);
            disableHandles();

            return {
                updateVisible: updateVisible,
                update: update,
                release: release,
                refresh: refresh,
                isAwake: function() {
                    return awake;
                },
                setCursor: function(cursor) {
                    $track.css('cursor', cursor);
                },
                enableHandles: enableHandles,
                enableOnly: function() {
                    seehandles = true;
                },
                showHandles: showHandles,
                disableHandles: disableHandles,
                animMode: animMode,
                done: done
            };
        }());

        //}}}
        // Tracker Module {{{
        var Tracker = (function() {
            var onMove = function() {
            },
                    onDone = function() {
                    },
                    trackDoc = options.trackDocument;

            function toFront() //{{{
            {
                $trk.css({
                    zIndex: 450
                });
                if (trackDoc) {
                    $(document)
                            .bind('mousemove', trackMove)
                            .bind('mouseup', trackUp);
                }
            }
            //}}}
            function toBack() //{{{
            {
                $trk.css({
                    zIndex: 290
                });
                if (trackDoc) {
                    $(document)
                            .unbind('mousemove', trackMove)
                            .unbind('mouseup', trackUp);
                }
            }
            //}}}
            function trackMove(e) //{{{
            {
                onMove(mouseAbs(e));
                return false;
            }
            //}}}
            function trackUp(e) //{{{
            {
                e.preventDefault();
                e.stopPropagation();

                if (btndown) {
                    btndown = false;

                    onDone(mouseAbs(e));

                    if (Selection.isAwake()) {
                        options.onSelect.call(api, unscale(Coords.getFixed()));
                    }

                    toBack();
                    onMove = function() {
                    };
                    onDone = function() {
                    };
                }

                return false;
            }
            //}}}
            function activateHandlers(move, done) //{{{
            {
                btndown = true;
                onMove = move;
                onDone = done;
                toFront();
                return false;
            }
            //}}}
            function trackTouchMove(e) //{{{
            {
                e.pageX = e.originalEvent.changedTouches[0].pageX;
                e.pageY = e.originalEvent.changedTouches[0].pageY;
                return trackMove(e);
            }
            //}}}
            function trackTouchEnd(e) //{{{
            {
                e.pageX = e.originalEvent.changedTouches[0].pageX;
                e.pageY = e.originalEvent.changedTouches[0].pageY;
                return trackUp(e);
            }
            //}}}
            function setCursor(t) //{{{
            {
                $trk.css('cursor', t);
            }
            //}}}

            if (Touch.support) {
                $(document)
                        .bind('touchmove', trackTouchMove)
                        .bind('touchend', trackTouchEnd);
            }

            if (!trackDoc) {
                $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
            }

            $img.before($trk);
            return {
                activateHandlers: activateHandlers,
                setCursor: setCursor
            };
        }());
        //}}}
        // KeyManager Module {{{
        var KeyManager = (function() {
            var $keymgr = $('<input type="radio" />').css({
                position: 'fixed',
                left: '-120px',
                width: '12px'
            }),
            $keywrap = $('<div />').css({
                position: 'absolute',
                overflow: 'hidden'
            }).append($keymgr);

            function watchKeys() //{{{
            {
                if (options.keySupport) {
                    $keymgr.show();
                    $keymgr.focus();
                }
            }
            //}}}
            function onBlur(e) //{{{
            {
                $keymgr.hide();
            }
            //}}}
            function doNudge(e, x, y) //{{{
            {
                if (options.allowMove) {
                    Coords.moveOffset([x, y]);
                    Selection.updateVisible();
                }
                e.preventDefault();
                e.stopPropagation();
            }
            //}}}
            function parseKey(e) //{{{
            {
                if (e.ctrlKey) {
                    return true;
                }
                shift_down = e.shiftKey ? true : false;
                var nudge = shift_down ? 10 : 1;

                switch (e.keyCode) {
                    case 37:
                        doNudge(e, -nudge, 0);
                        break;
                    case 39:
                        doNudge(e, nudge, 0);
                        break;
                    case 38:
                        doNudge(e, 0, -nudge);
                        break;
                    case 40:
                        doNudge(e, 0, nudge);
                        break;
                    case 27:
                        Selection.release();
                        break;
                    case 9:
                        return true;
                }

                return false;
            }
            //}}}

            if (options.keySupport) {
                $keymgr.keydown(parseKey).blur(onBlur);
                if (ie6mode || !options.fixedSupport) {
                    $keymgr.css({
                        position: 'absolute',
                        left: '-20px'
                    });
                    $keywrap.append($keymgr).insertBefore($img);
                } else {
                    $keymgr.insertBefore($img);
                }
            }


            return {
                watchKeys: watchKeys
            };
        }());
        //}}}
        // }}}
        // API methods {{{
        function setClass(cname) //{{{
        {
            $div.removeClass().addClass(cssClass('holder')).addClass(cname);
        }
        //}}}
        function animateTo(a, callback) //{{{
        {
            var x1 = parseInt(a[0], 10) / xscale,
                    y1 = parseInt(a[1], 10) / yscale,
                    x2 = parseInt(a[2], 10) / xscale,
                    y2 = parseInt(a[3], 10) / yscale;

            if (animating) {
                return;
            }

            var animto = Coords.flipCoords(x1, y1, x2, y2),
                    c = Coords.getFixed(),
                    initcr = [c.x, c.y, c.x2, c.y2],
                    animat = initcr,
                    interv = options.animationDelay,
                    ix1 = animto[0] - initcr[0],
                    iy1 = animto[1] - initcr[1],
                    ix2 = animto[2] - initcr[2],
                    iy2 = animto[3] - initcr[3],
                    pcent = 0,
                    velocity = options.swingSpeed;

            x = animat[0];
            y = animat[1];
            x2 = animat[2];
            y2 = animat[3];

            Selection.animMode(true);
            var anim_timer;

            function queueAnimator() {
                window.setTimeout(animator, interv);
            }
            var animator = (function() {
                return function() {
                    pcent += (100 - pcent) / velocity;

                    animat[0] = x + ((pcent / 100) * ix1);
                    animat[1] = y + ((pcent / 100) * iy1);
                    animat[2] = x2 + ((pcent / 100) * ix2);
                    animat[3] = y2 + ((pcent / 100) * iy2);

                    if (pcent >= 99.8) {
                        pcent = 100;
                    }
                    if (pcent < 100) {
                        setSelectRaw(animat);
                        queueAnimator();
                    } else {
                        Selection.done();
                        if (typeof (callback) === 'function') {
                            callback.call(api);
                        }
                    }
                };
            }());
            queueAnimator();
        }
        //}}}
        function setSelect(rect) //{{{
        {
            setSelectRaw([
                parseInt(rect[0], 10) / xscale, parseInt(rect[1], 10) / yscale, parseInt(rect[2], 10) / xscale, parseInt(rect[3], 10) / yscale]);
        }
        //}}}
        function setSelectRaw(l) //{{{
        {
            Coords.setPressed([l[0], l[1]]);
            Coords.setCurrent([l[2], l[3]]);
            Selection.update();
        }
        //}}}
        function tellSelect() //{{{
        {
            return unscale(Coords.getFixed());
        }
        //}}}
        function tellScaled() //{{{
        {
            return Coords.getFixed();
        }
        //}}}
        function setOptionsNew(opt) //{{{
        {
            setOptions(opt);
            interfaceUpdate();
        }
        //}}}
        function disableCrop() //{{{
        {
            options.disabled = true;
            Selection.disableHandles();
            Selection.setCursor('default');
            Tracker.setCursor('default');
        }
        //}}}
        function enableCrop() //{{{
        {
            options.disabled = false;
            interfaceUpdate();
        }
        //}}}
        function cancelCrop() //{{{
        {
            Selection.done();
            Tracker.activateHandlers(null, null);
        }
        //}}}
        function destroy() //{{{
        {
            $div.remove();
            $origimg.show();
            $(obj).removeData('Jcrop');
        }
        //}}}
        function setImage(src, callback) //{{{
        {
            Selection.release();
            disableCrop();
            var img = new Image();
            img.onload = function() {
                var iw = img.width;
                var ih = img.height;
                var bw = options.boxWidth;
                var bh = options.boxHeight;
                $img.width(iw).height(ih);
                $img.attr('src', src);
                $img2.attr('src', src);
                presize($img, bw, bh);
                boundx = $img.width();
                boundy = $img.height();
                $img2.width(boundx).height(boundy);
                $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2));
                $div.width(boundx).height(boundy);
                enableCrop();

                if (typeof (callback) === 'function') {
                    callback.call(api);
                }
            };
            img.src = src;
        }
        //}}}
        function interfaceUpdate(alt) //{{{
                // This method tweaks the interface based on options object.
                        // Called when options are changed and at end of initialization.
                        {
                            if (options.allowResize) {
                                if (alt) {
                                    Selection.enableOnly();
                                } else {
                                    Selection.enableHandles();
                                }
                            } else {
                                Selection.disableHandles();
                            }

                            Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
                            Selection.setCursor(options.allowMove ? 'move' : 'default');


                            if (options.hasOwnProperty('setSelect')) {
                                setSelect(options.setSelect);
                                Selection.done();
                                delete(options.setSelect);
                            }

                            if (options.hasOwnProperty('trueSize')) {
                                xscale = options.trueSize[0] / boundx;
                                yscale = options.trueSize[1] / boundy;
                            }
                            if (options.hasOwnProperty('bgColor')) {

                                if (supportsColorFade() && options.fadeTime) {
                                    $div.animate({
                                        backgroundColor: options.bgColor
                                    }, {
                                        queue: false,
                                        duration: options.fadeTime
                                    });
                                } else {
                                    $div.css('backgroundColor', options.bgColor);
                                }

                                delete(options.bgColor);
                            }
                            if (options.hasOwnProperty('bgOpacity')) {
                                bgopacity = options.bgOpacity;

                                if (Selection.isAwake()) {
                                    if (options.fadeTime) {
                                        $img.fadeTo(options.fadeTime, bgopacity);
                                    } else {
                                        $div.css('opacity', options.opacity);
                                    }
                                }
                                delete(options.bgOpacity);
                            }

                            xlimit = options.maxSize[0] || 0;
                            ylimit = options.maxSize[1] || 0;
                            xmin = options.minSize[0] || 0;
                            ymin = options.minSize[1] || 0;

                            if (options.hasOwnProperty('outerImage')) {
                                $img.attr('src', options.outerImage);
                                delete(options.outerImage);
                            }

                            Selection.refresh();
                        }
                //}}}
                //}}}

                if (Touch.support) {
                    $trk.bind('touchstart', Touch.newSelection);
                }

                $hdl_holder.hide();
                interfaceUpdate(true);

                var api = {
                    setImage: setImage,
                    animateTo: animateTo,
                    setSelect: setSelect,
                    setOptions: setOptionsNew,
                    tellSelect: tellSelect,
                    tellScaled: tellScaled,
                    setClass: setClass,
                    disable: disableCrop,
                    enable: enableCrop,
                    cancel: cancelCrop,
                    release: Selection.release,
                    destroy: destroy,
                    focus: KeyManager.watchKeys,
                    getBounds: function() {
                        return [boundx * xscale, boundy * yscale];
                    },
                    getWidgetSize: function() {
                        return [boundx, boundy];
                    },
                    getScaleFactor: function() {
                        return [xscale, yscale];
                    },
                    ui: {
                        holder: $div,
                        selection: $sel
                    }
                };

                if ($.browser.msie) {
                    $div.bind('selectstart', function() {
                        return false;
                    });
                }

                $origimg.data('Jcrop', api);
                return api;
            };
    $.fn.Jcrop = function(options, callback) //{{{
    {

        function attachWhenDone(from) //{{{
        {
            var opt = (typeof (options) === 'object') ? options : {};
            var loadsrc = opt.useImg || from.src;
            var img = new Image();
            img.onload = function() {
                function attachJcrop() {
                    var api = $.Jcrop(from, opt);
                    if (typeof (callback) === 'function') {
                        callback.call(api);
                    }
                }

                function attachAttempt() {
                    if (!img.width || !img.height) {
                        window.setTimeout(attachAttempt, 50);
                    } else {
                        attachJcrop();
                    }
                }
                window.setTimeout(attachAttempt, 50);
            };
            img.src = loadsrc;
        }
        //}}}

        // Iterate over each object, attach Jcrop
        this.each(function() {
            // If we've already attached to this object
            if ($(this).data('Jcrop')) {
                // The API can be requested this way (undocumented)
                if (options === 'api') {
                    return $(this).data('Jcrop');
                }
                // Otherwise, we just reset the options...
                else {
                    $(this).data('Jcrop').setOptions(options);
                }
            }
            // If we haven't been attached, preload and attach
            else {
                attachWhenDone(this);
            }
        });

        // Return "this" so the object is chainable (jQuery-style)
        return this;
    };
    //}}}
    // Global Defaults {{{
    $.Jcrop.defaults = {
        // Basic Settings
        allowSelect: true,
        allowMove: true,
        allowResize: true,
        trackDocument: true,
        // Styling Options
        baseClass: 'jcrop',
        addClass: null,
        bgColor: 'black',
        bgOpacity: 0.6,
        bgFade: false,
        borderOpacity: 0.4,
        handleOpacity: 0.5,
        handleSize: 9,
        handleOffset: 5,
        aspectRatio: 0,
        keySupport: true,
        cornerHandles: true,
        sideHandles: true,
        drawBorders: true,
        dragEdges: true,
        fixedSupport: true,
        touchSupport: null,
        boxWidth: 0,
        boxHeight: 0,
        boundary: 2,
        fadeTime: 400,
        animationDelay: 20,
        swingSpeed: 3,
        minSelect: [0, 0],
        maxSize: [0, 0],
        minSize: [0, 0],
        // Callbacks / Event Handlers
        onChange: function() {
        },
        onSelect: function() {
        },
        onRelease: function() {
        }
    };

    // }}}
}(jQuery));
